<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>面试题2021/3/5</title>
    <link rel="stylesheet" href="css/mian.css">
    <script crossorigin src="js/react.development.js"></script>
    <script crossorigin src="js/react-dom.development.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
    <style>

    </style>
</head>

<body>
    <div id="example"></div>

    <script type="text/babel">


        const ExchangeRateApi = {
            exchangeRate: null, 
            CNY: null,
            RUB: null,
            USD: null,
            subscriptionCallBack: null,

            setExchangeRate: (data) => {
                this.exchangeRate = data;
            },

            calculate: (type, prices) => {
                const exchangeRate = this.exchangeRate;
                let price = parseFloat(prices);
                // 汇率换算
                switch (type) {
                    case 'CNY':
                        this.CNY = price;
                        this.RUB = (price * exchangeRate.RUB.value);
                        this.USD = (price * exchangeRate.USD.value);
                        break;
                    case 'RUB':
                        this.RUB = price;
                        this.CNY = (price / exchangeRate.RUB.value);
                        this.USD = (price * (exchangeRate.USD.value / exchangeRate.RUB.value));
                        break;
                    case 'USD':
                        this.USD = price;
                        this.RUB = (price * (exchangeRate.RUB.value / exchangeRate.USD.value));
                        this.CNY = (price / exchangeRate.USD.value);
                        break;
                }
                const callBack = {
                    CNY: numFormat(this.CNY),
                    RUB: numFormat(this.RUB),
                    USD: numFormat(this.USD)
                }
                if (this.subscriptionCallBack) {
                    this.subscriptionCallBack(callBack)
                }
                return callBack;
            },
            //订阅汇率
            subscriptionExchangeRate: (callBack) => {
                this.subscriptionCallBack = callBack;
            },
            //取消订阅
            unsubscribeExchangeRate: () => {
                this.callBack = null;
            }
        }

        const ActionApi = {
            task: null,
            price: null,
            moneyType: null,
            priceRate: null,

            taskIncident: null,
            priceIncident: null,
            moneyTypeIncident: null,

            taskChange: (event, incident) => {
                this.task = event.target.value;
                this.taskIncident = incident;
                incident(event.target.value);
            },

            priceChange: (event, incident) => {
                this.price = event.target.value;
                this.priceIncident = incident;
                if (this.moneyType && this.price) {
                    this.priceRate = ExchangeRateApi.calculate(this.moneyType, this.price, this.setExchange);
                }
                incident(event.target.value);
            },
            moneyTypeChange: (event, incident) => {
                this.moneyType = event.target.value;
                this.moneyTypeIncident = incident;
                if (this.price && this.moneyType) {
                    this.priceRate = ExchangeRateApi.calculate(this.moneyType, this.price, this.setExchange);
                }
                incident(event.target.value);
            },
            addTask: () => {
                if (!this.task) {
                    ToastWarning('请输入任务', 2000);
                } else if (!this.price) {
                    ToastWarning('请输入价格', 2000);
                } else if (!this.pric && isNaN(parseFloat(this.price))) {;
                    ToastWarning('价格请输入数值类型', 2000);
                } else if (!this.moneyType) {
                    console.log()
                    ToastWarning('请选择货币类型', 2000);
                } else {
                    
                    const obj = {
                        timestamp: new Date().getTime(),
                        isComplete: false,
                        taskName: task,
                        taskPrice: price,
                        CNY: this.priceRate.CNY,
                        RUB: this.priceRate.RUB,
                        USD: this.priceRate.USD
                    }
                    //制空
                    this.task = null;
                    this.price = null;
                    this.moneyType = null;
                    this.taskIncident('');
                    this.priceIncident('');
                    this.moneyTypeIncident('');
                    ExchangeRateApi.calculate(this.moneyType, 0, this.setExchange);
                    return obj;
                }
            }
        }

        //警告toast
        function ToastWarning (msg, duration){
            duration = isNaN(duration) ? 3000 : duration;
            let m = document.createElement('div');
            m.innerHTML = msg;
            m.className = 'toast-warning';
            document.body.appendChild(m);
            setTimeout(function () {
                let d = 0.5;
                m.style.webkitTransition = `-webkit-transform ${d}s ease-in, opacity ${d}s ease-in`;
                m.style.opacity = '0';
                setTimeout(function () { document.body.removeChild(m) }, d * 1000);
            }, duration);
        }

        {/* 任务输入组件 */ }
        function TaskInput(props) {
            const [task, setTask] = React.useState(''); {/* 任务 */ }
            const valueChange = React.useCallback(
                (val, verify = true, massage) => {
                    setTask(val);
                }, [])
            return (<div>
                <input className='com-input' value={task} type="text" placeholder='任务' onChange={(e) => { ActionApi.taskChange(e, valueChange) }} />
            </div>)
        }

        {/* 价格输入组件 */ }
        function PriceInput(props) {
            const [price, setPrice] = React.useState(''); {/* 价格 */ }
            const valueChange = React.useCallback(
                (val, verify) => {
                    setPrice(val);
                }, [])
            return (<input className='com-input price-input' value={price} type="text" placeholder='价格' onChange={(e) => { ActionApi.priceChange(e, valueChange) }} />)
        }

        {/* 货币类型选择组件 */ }
        function MoneyTypeSelect(props) {
            const [moneyType, setMoneyType] = React.useState(''); {/* 货币类型 */ }
            const valueChange = React.useCallback(
                (val) => {
                    setMoneyType(val);
                }, [])
            return (
                <div className='com-select' >
                    <select value={moneyType} className={moneyType === '' ? 'default-option' : ''} onChange={(e) => { ActionApi.moneyTypeChange(e, valueChange) }}>
                        <option disabled value="">货币类型</option>
                        <option value="CNY">人民币</option>
                        <option value="RUB">卢布</option>
                        <option value="USD">美元</option>
                    </select>
                </div>
            )
        }

        //数值格式化
        function numFormat(num, n = 4) {
            return Math.round(num * Math.pow(10, n)) / Math.pow(10, n);
        }

        //价格面板
        function CurrencyPanel(props) {
            const [CNY, setCNY] = React.useState(0);
            const [RUB, setRUB] = React.useState(0);
            const [USD, setUSD] = React.useState(0);
            function rateChange(e) {
                setCNY(e.CNY);
                setRUB(e.RUB);
                setUSD(e.USD);
            }
            React.useEffect(() => {
                ExchangeRateApi.setExchangeRate(props.exchangeRate);
                ExchangeRateApi.subscriptionExchangeRate(rateChange);
            }, [props.exchangeRate])

            return (<div>
                <div className='flex justify-between'>
                    <div>
                        <span>当前汇率：</span>
                    </div>
                    <div className='rate-box'>
                        <span>{props.exchangeRate.RUB ? numFormat(1 / props.exchangeRate.RUB.value) : 0} ₽/¥</span>
                        <span>{props.exchangeRate.USD ? numFormat((1 / (props.exchangeRate.RUB.value / props.exchangeRate.USD.value))) : 0} ₽/$</span>
                        <span>{props.exchangeRate.USD ? numFormat(props.exchangeRate.USD.value) : 0} ¥/$</span>
                    </div>
                </div>
                <div className='flex justify-between padding-xs-tb'>
                    <div>
                        <span>任务价格：</span>
                    </div>
                    <div className='rate-box'>
                        <span>{CNY}&nbsp;¥</span>
                        <span>{RUB}&nbsp;₽</span>
                        <span>{USD}&nbsp;$</span>
                    </div>
                </div>
            </div>)
        }

        //头部面板组件
        const HeaderPanel = React.memo(({ exchangeRate, changeTableData }) => {
            React.useEffect(() => {
                console.log('渲染了HeaderPanel')
            })
            const btnClike = React.useCallback(
                () => {
                    const data = ActionApi.addTask();
                    //将参数传递给父组件 设置state
                    if (data) changeTableData(data);
                }, [])
            return (
                <div>
                    <div className='orer-box flex align-center'>
                        <TaskInput></TaskInput>
                        <PriceInput></PriceInput>
                        <MoneyTypeSelect></MoneyTypeSelect>
                        <button className='com-btn' onClick={btnClike}>添加</button>
                    </div>
                    <div>
                        <CurrencyPanel exchangeRate={exchangeRate} />
                    </div>
                </div>)
        })

        //计算对象数组中某个属性合计
        function countPrice(arr, keyName) {
            let price = 0;
            price = arr.reduce(function (total, currentValue, currentIndex, arr) {
                return currentValue[keyName] ? (total + currentValue[keyName]) : total;
            }, 0);
            return price;
        }

        function Tables({ title, resultTitle, tableData, taskListChange }) {
            const taksChange = React.useCallback(
                (e, timestamp) => {
                    taskListChange(timestamp);
                })
            let dom = [];
            for (let index in tableData) {
                dom.push(<tr key={index} className='cus-tr'>
                    <td>
                        <input type="checkbox" name="reading" checked={tableData[index].isComplete} onChange={(e) => { taksChange(e, tableData[index].timestamp) }} />
                        <span className={tableData[index].isComplete ? 'del-task' : ''}>{tableData[index].taskName}</span>
                    </td>
                    <td>₽ {tableData[index].RUB}</td>
                    <td>$ {tableData[index].USD}</td>
                    <td>¥ {tableData[index].CNY}</td>
                </tr>);
            }
            return (<table>
                <thead>
                    <tr>
                        <td>{title}：</td>
                    </tr>
                </thead>
                <tbody>
                    {dom}
                </tbody>
                <tfoot>
                    <tr className='cus-tfoot'>
                        <td>{resultTitle}：</td>
                        <td>₽ {numFormat(countPrice(tableData, 'RUB'))} </td>
                        <td>$ {numFormat(countPrice(tableData, 'USD'))}</td>
                        <td>¥ {numFormat(countPrice(tableData, 'CNY'))}</td>
                    </tr>
                </tfoot>
            </table>)
        }

        //任务面板
        const TaskTables = React.memo(({ task }) => {
            const [taskList, setTaskList] = React.useState([]);
            React.useEffect(() => {
                if (task) setTaskList([task, ...taskList]);
            }, [task])

            function taskListChange(timestamp) {
                let copyTaskList = taskList;//copa一份
                copyTaskList.find((item) => {
                    if (item.timestamp === timestamp) item.isComplete = !item.isComplete;
                })
                setTaskList([...copyTaskList]);
            }
            let toBeCompletedTask = [];
            let completedTask = [];
            for (let obj of taskList) {
                if (!obj.isComplete) {
                    toBeCompletedTask.push(obj);
                } else {
                    completedTask.push(obj);
                }
            }
            return (
                <div>
                    <Tables title='计划' resultTitle='将要花费' tableData={toBeCompletedTask} taskListChange={taskListChange}></Tables>
                    <Tables title='已完成' resultTitle='一共花了' tableData={completedTask} taskListChange={taskListChange}></Tables>
                </div>)
        })

        function App() {
            const [exchangeRate, setExchangeRate] = React.useState({});
            const [task, setTask] = React.useState(null);
            const changeTableData = React.useCallback(
                (data) => {
                    setTask(data);
                }, [])

            React.useEffect(() => {
                console.log('iniBiz')
                fetch('https://api.globus.furniture/forex').then(res => res.json(), err => {
                    //网络错误、连接失败、无法解析对方数据
                    console.error('request error', err);
                }).then(data => {
                    setExchangeRate(data);
                })
            }, [])

            React.useEffect(() => {
                console.log()
            })
            return (
                <div className='main-box'>
                    <HeaderPanel exchangeRate={exchangeRate} changeTableData={changeTableData}></HeaderPanel>
                    <TaskTables task={task}></TaskTables>
                </div>
            )
        }


        const element = <App />;
        ReactDOM.render(
            element,
            document.getElementById('example')
        );
    </script>
</body>

</html>